package org.bdware.doip.core.codec.packet;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageCodec;
import org.apache.log4j.Logger;
import org.bdware.doip.core.doipMessage.DoipMessage;
import org.bdware.doip.core.doipMessage.MessageEnvelope;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class PacketNettyUDPCodec extends MessageToMessageCodec<DatagramPacket, DoipMessage> {

    Logger logger = Logger.getLogger(PacketNettyUDPCodec.class);
    PacketMessageCodec codec = new PacketMessageCodecImpl();
    HashMap<Integer,AbstractEnvelopeReceiveBuffer> receiveBufferMap = new HashMap<>();
    HashMap<Integer,AbstractEnvelopeSendBuffer> sendBufferMap = new HashMap<>();

    @Override
    protected void encode(ChannelHandlerContext ctx, DoipMessage msg, List<Object> out){
        logger.debug("encoding DOIP message to UDP Packets");
//        logger.debug("encoding msg body: " + new String(msg.body.getEncodedData()));
        try {

            if(sendBufferMap.get(msg.requestID) != null){
                logger.error("duplicate requestID");
                return;
            }
            NoAckEnvelopeSendBuffer senderBuffer = new NoAckEnvelopeSendBuffer(msg.requestID);

            ArrayList<MessageEnvelope> envelpoes = codec.MessageToEnvelopes(msg);
            for(MessageEnvelope env:envelpoes){
                ByteBuf buff = Unpooled.directBuffer();
                senderBuffer.pushToBuffer(env);
                buff.writeBytes(codec.EnvelopeToBytes(env));
                sendBufferMap.put(msg.requestID,senderBuffer);
                out.add(new DatagramPacket(buff,msg.getSender()));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List<Object> out){
        logger.debug("decoding UDP Packets to doip message");
        try {
            ByteBuf bf = msg.content();
            byte[] envBytes = new byte[bf.readableBytes()];
            bf.readBytes(envBytes);
            MessageEnvelope envelope = codec.BytesToEnvelope(envBytes);
            envelope.setSender(msg.sender());
            //如果是重传消息
            if(envelope.isResend()){
//                logger.info("receive resend message, sequenceNumber: " + envelope.sequenceNumber);
//                logger.info("receive resend message, requestID: " + envelope.requestId);
                //如果收到的是完成确认消息
                if(sendBufferMap.get(envelope.requestId)!=null && sendBufferMap.get(envelope.requestId).receiveAck(envelope)){
//                    logger.debug("got completed ack: requestID: " + envelope.requestId);
                    sendBufferMap.remove(envelope.requestId);
                }

            }else{
                AbstractEnvelopeReceiveBuffer recvBuffer = receiveBufferMap.get(envelope.requestId);
                if(recvBuffer == null){
                    recvBuffer = new NoResendEnvelopeReceiveBuffer(envelope.requestId,ctx);
                    receiveBufferMap.put(envelope.requestId,recvBuffer);
                }
                recvBuffer.addEnvelope(envelope);
                if(recvBuffer.isCompleted()){
                    ArrayList<MessageEnvelope> envList = recvBuffer.getSortedEnvelopes();
                    out.add(codec.EnvelopesToMessage(envList));
//                    logger.debug("message completed requestID: " + envelope.requestId);
                };
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
